package scalalab.processors

import scala.actors.{InputChannel, OutputChannel}
import scalalab.core.UserMessages._
import scalalab.core.InterpreterMessages._
import javax.swing.JFrame
import scala.actors.Actor
import Actor._
import java.io.{PrintWriter, PipedWriter, PipedReader}
import scala.tools.nsc.Interpreter

import scalaExec.Interpreter._


class ScalaProcessor(private val p : Actor) {

 import  ScalaProcessor._
  
  import scala.tools.nsc.{Interpreter, Settings, InterpreterResults}
  
    
  case class ResultText(res : String)
  case class Restart()
  case class RestartEJML()
  
  private[scalalab] def restart() {
     commandProc ! Restart
  }
  
  private[scalalab] def restartEJML() {
     commandProc ! RestartEJML
  }
  
  private[scalalab] def process(cmd : ProcessCell) {
    commandProc ! cmd
  }
 
      

  //This actor processes all commands that come across
  //from the interpreter
  private val commandProc = actor {
    // matrixType controls which library to initialize
    class InterpWrapper(matrixType: Int) {
      private val pipe = new PipedWriter
      private val writer = new PrintWriter(pipe)
      
      private val IDESettings = new Settings
      
      _root_.scalalab.JavaUtilities.detectPaths
       
      IDESettings.classpath.append(scalalab.JavaGlobals.jarFilePath)  // the Scalalab .jar file by itself
      IDESettings.classpath.append(scalalab.JavaGlobals.compFile)  // Scala Compiler
      IDESettings.classpath.append(scalalab.JavaGlobals.libFile)     // Scala Libraries
      IDESettings.classpath.append(scalalab.JavaGlobals.swingFile)  // Scala Swing
      IDESettings.classpath.append(scalalab.JavaGlobals.libsFile)   // ScalaLab main libraries file
      IDESettings.classpath.append(scalalab.JavaGlobals.VISADFile)   // VISAD visualization library

      // interpret different imports depending on the library
      if (matrixType == GlobalValues.JAMAMat) {
      theCellsInterpreter = new scala.tools.nsc.Interpreter(IDESettings, writer)
      theCellsInterpreter.interpret(_root_.scalaExec.Interpreter.GlobalValues.basicImportsScala)
      writer.flush   // drop the interpreter's output from tyhe imports
      }
      else {
      theCellsInterpreter = new scala.tools.nsc.Interpreter(IDESettings, writer)
      theCellsInterpreter.interpret(_root_.scalaExec.Interpreter.GlobalValues.basicImportsEJMLScala)
      theCellsInterpreter.interpret(_root_.scalaExec.Interpreter.GlobalValues.scalaPlusEJMLImports)
      writer.flush   // drop the interpreter's output from tyhe imports
      }
 
      // connect now the reader in order to avoid to take the output from importing the global imports
      private val reader = new PipedReader
      pipe.connect(reader)
      
      def interpret(command : String) : InterpreterResults.Result = theCellsInterpreter.interpret(command)
      
      def closeInterpreter = {
        theCellsInterpreter.close
        reader.close
      }
      
      def hasResult : Boolean = {
        reader.ready
      }
      
      def getResult : String = { 
        reader.read match {
        case -1 =>
          //We are done with this actor, we can exit it
          println("Reader dead, exiting: 0")
          exit
        case x =>
          val sb = new StringBuilder
          val c = x.asInstanceOf[Char]
          sb.append(c)
          def readMore {
            if (reader.ready) {
              reader.read match {
              case -1 =>
                println("Reader dead, exiting: 1")
              case x =>
                val c = x.asInstanceOf[Char]
                sb.append(c)
              }
            }
            if (reader.ready) {
              readMore
            } else {
              //Hack to fix issues with how the interpreter 
              //flushes its outputs.
              Thread.sleep(50)
              if (reader.ready) {
                readMore
              }
            }
          }
          readMore
          sb.toString
        }
      }
      
      
    }  // InterpWrapper
    
    // create a ScalaLab interpreter based on the default 0-indexed Matrix type
     interp = new InterpWrapper(GlobalValues.JAMAMat)
     
     
    def restartEJML  {
      interp.asInstanceOf[InterpWrapper].closeInterpreter
      interp = new InterpWrapper(GlobalValues.EJMLMat)
      var toolbarFrame = new JFrame("EJML - Mat ")
      var matToolbar =  new scalaExec.gui.MatScalaOperationsToolbar 
      toolbarFrame.add(matToolbar)
      toolbarFrame.setSize(400, 200)
      toolbarFrame.setLocation(400, 400)
      toolbarFrame.setVisible(true)
      }
   	def restart  {
        interp.asInstanceOf[InterpWrapper].closeInterpreter
        interp = new InterpWrapper(GlobalValues.JAMAMat)
    }

    loop {     // the loop of the commandProc actor
      receive {
        case command : ProcessCell =>
          import InterpreterResults._
          def interpret(x: String) = {
            interp.asInstanceOf[InterpWrapper].interpret(x) match {
            case Success | Error =>
              def getResult : String = {
                if (interp.asInstanceOf[InterpWrapper].hasResult) {
                  //Here return the result, less the line end
                  interp.asInstanceOf[InterpWrapper].getResult.stripLineEnd
                } else {
                  "<No Result>"
                }
              }  // getResult
              InterpResult(command, getResult)
            case Incomplete =>
              //Do Nothing!
              //Just let the interpreter keep waiting
              InterpResult(command, "<Incomplete Expression>")
            }  // match
          }    // interpret
          
          import scala.util.matching.Regex
          val loadRegex = new Regex("""\s*:load\s+(.*)\s*""", "filename")
          val readRegex = new Regex("""\s*:read\s+(.*)\s*""", "filename")

          def read(filename : String) (fn : (String) => InterpResult) : InterpResult = {
            import io.Source
            import java.io.{FileNotFoundException, IOException}

            try {
              val sb = new StringBuilder;
              Source.fromFile(new java.io.File(filename)).getLines().foreach(sb.append(_))
              fn(sb.toString)
            } catch {
            case e : FileNotFoundException => 
              InterpResult(command, "<Unable to load file, Not Found:[" + filename + "]>")
            case e : IOException => 
              InterpResult(command, "<Unable to load file, IOException: " + e + ">")
            case e : Throwable => 
              InterpResult(command, "<Unable to load file: " + e + ">")
            }
          }
          
          command.text match {
          case `loadRegex`(filename) =>
            p ! read(filename) {interpret(_)}
          case `readRegex`(filename) =>
            p ! read(filename) {InterpResult(command, _)}
          case x =>
            p ! interpret(x)
          }
        case Restart =>
          restart
        case RestartEJML =>
          restartEJML
      }
    }
  }
}

object ScalaProcessor  {
        private var interp: AnyRef = _
        var theCellsInterpreter: scala.tools.nsc.Interpreter = _
        
}